package cc.admin.modules.bi.service.impl;

import cc.admin.common.db.table.base.EtlReader;
import cc.admin.common.db.table.entity.DbColumn;
import cc.admin.common.db.table.entity.DbEntity;
import cc.admin.modules.bi.entity.PieData;
import cc.admin.modules.bi.entity.SeriesData;
import cc.admin.modules.bi.entity.View;
import cc.admin.modules.bi.mapper.ViewMapper;
import cc.admin.modules.bi.model.*;
import cc.admin.modules.bi.service.IViewService;
import cc.admin.modules.bi.util.SqlUtil;
import cc.admin.modules.sys.mapper.SysDataSourceMapper;
import com.alibaba.druid.util.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @Description: bi_view
 * @Author: ZhangHouYing
 * @Date: 2020-07-04
 * @Version: V1.0
 */
@Service
public class ViewServiceImpl extends ServiceImpl<ViewMapper, View> implements IViewService {

	@Autowired
	ViewMapper viewMapper;

	@Autowired
	private SysDataSourceMapper sysDataSourceMapper;

	@Value("${sql_template_delimiter:$}")
	private String sqlTempDelimiter;

	private String dbVersion = null;

	@Override
	public Page<Map<String, Object>> getData(String id, ViewExecuteParam executeParam) {
		View view = viewMapper.selectById(id);
		EtlReader etlReader = EtlReader.getInstance(view.getSourceId());
		Page<Map<String, Object>> paginate = new Page<>();
		if (StringUtils.isEmpty(view.getViewSql())) {
			return paginate;
		}
		String sql = getSql(view, executeParam);
		return etlReader.getPageData(sql, executeParam.getPageNo(), executeParam.getPageSize());
	}

	public String getSql(View view, ViewExecuteParam executeParam) {
		return SqlUtil.renderSql(view.getViewSql(), executeParam);
	}

	@Override
	public PaginateWithQueryColumns executeSql(ViewExecuteSql executeSql) {
		PaginateWithQueryColumns paginateWithQueryColumns = new PaginateWithQueryColumns();
		EtlReader etlReader = EtlReader.getInstance(executeSql.getSourceId());
		Page<Map<String, Object>> page = etlReader.getPageData(executeSql.getSql(), executeSql.getPageNo(), executeSql.getPageSize());
		paginateWithQueryColumns.setTotal(page.getTotal());
		paginateWithQueryColumns.setRecords(page.getRecords());
		etlReader.getPageDataSql(executeSql.getSql(), executeSql.getPageNo(), executeSql.getPageSize());
		etlReader.getJdbcTemplate().query(executeSql.getSql(), rs -> {
			if (null == rs) {
				return paginateWithQueryColumns;
			}
			ResultSetMetaData metaData = rs.getMetaData();
			List<QueryColumn> queryColumns = new ArrayList<>();
			for (int i = 1; i <= metaData.getColumnCount(); i++) {
				String key = metaData.getColumnLabel(i);
				queryColumns.add(new QueryColumn(key, metaData.getColumnTypeName(i)));
			}
			paginateWithQueryColumns.setColumns(queryColumns);
			return paginateWithQueryColumns;
		});
		return paginateWithQueryColumns;
	}

	@Override
	public DbEntity columns(String dbKey, String tableName) {
		EtlReader etlReader = EtlReader.getInstance(dbKey);
		DbEntity entity = etlReader.readEntity(tableName);
		List<DbColumn> columnList = etlReader.readColumnList(tableName);
		entity.setDbColumnList(columnList);
		return entity;
	}

	@Override
	public Map<String, Object> getTableData(String id, ViewExecuteParam executeParam) {
		Map<String, Object> resultMap = Maps.newHashMap();
		Page<Map<String, Object>> resultPage = getData(id, executeParam);
		List<Map<String, Object>> records = resultPage.getRecords();
		List<Group> groups = executeParam.getGroups();
		List<Aggregator> aggregators = executeParam.getAggregators();
		List<Object> headList = Lists.newArrayList();
		List<List<Object>> bodyList = Lists.newArrayList();
		//水平显示
		if (executeParam.getHorizontal()) {
			for (Group group : groups) {
				headList.add(group.getDisplayName());
			}
			Map<String, List<Object>> map = Maps.newHashMap();
			for (Aggregator aggregator : aggregators) {
				List<Object> dataList = Lists.newArrayList();
				dataList.add(aggregator.getDisplayName());
				map.put(aggregator.getName(), dataList);
			}
			for (int i = 0; i < records.size(); i++) {
				Map<String, Object> record = records.get(i);
				for (Group group : groups) {
					headList.add(record.get(group.getName()));
				}
				for (Aggregator aggregator : aggregators) {
					map.get(aggregator.getName()).add(record.get(aggregator.getName()));
				}
			}
			for (Aggregator aggregator : aggregators) {
				bodyList.add(map.get(aggregator.getName()));
			}
		} else {
			for (Group group : groups) {
				headList.add(group.getDisplayName());
			}
			for (Aggregator aggregator : aggregators) {
				headList.add(aggregator.getDisplayName());

			}
			for (int i = 0; i < records.size(); i++) {
				Map<String, Object> record = records.get(i);
				List<Object> dataList = Lists.newArrayList();
				for (Group group : groups) {
					dataList.add(record.get(group.getName()));
				}
				for (Aggregator aggregator : aggregators) {
					dataList.add(record.get(aggregator.getName()));
				}
				bodyList.add(dataList);
				//控制下获取数据个数
				if (i + 1 >= executeParam.getPageSize()) {
					break;
				}
			}
		}
		resultMap.put("head", headList);
		resultMap.put("body", bodyList);
		return resultMap;
	}

	@Override
	public Map<String, Object> getChartData(String id, ViewExecuteParam executeParam) {
		Map<String, Object> resultMap = Maps.newHashMap();
		if ("".equals(executeParam.getType())) {
		} else if ("pie".equals(executeParam.getType())) {
			resultMap = getPieData(id, executeParam);
		} else if ("donut".equals(executeParam.getType())) {
			resultMap = getDonutData(id, executeParam);
		} else if ("radar".equals(executeParam.getType())) {
			resultMap = getRadarData(id, executeParam);
		} else if ("funnel".equals(executeParam.getType())) {
			resultMap = getFunnelData(id, executeParam);
		} else if ("wordCloud".equals(executeParam.getType())) {
			resultMap = getWordCloudData(id, executeParam);
		} else if ("gauge".equals(executeParam.getType())) {
			resultMap = getGaugeData(id, executeParam);
		} else if ("text".equals(executeParam.getType())) {
			resultMap = getTextData(id, executeParam);
		} else if ("maps".equals(executeParam.getType())) {
			resultMap = getMapsData(id, executeParam);
		} else {
			resultMap = getLineData(id, executeParam);
		}
		return resultMap;
	}

	private Map<String, Object> getDonutData(String id, ViewExecuteParam executeParam) {
		Page<Map<String, Object>> resultPage = getData(id, executeParam);
		List<Map<String, Object>> records = resultPage.getRecords();
		Map<String, Object> resultMap = Maps.newHashMap();
		List<String> legend = Lists.newArrayList();
		List<Group> groups = executeParam.getGroups();
		List<Aggregator> aggregators = executeParam.getAggregators();
		//一个指标对应一个Data
		Map<String, SeriesData> seriesDataMap = Maps.newHashMap();
		List<PieData> seriesDataList = Lists.newArrayList();
		for (Aggregator aggregator : aggregators) {
			legend.add(aggregator.getDisplayName());
		}
		for (int i = 0; i < records.size(); i++) {
			Map<String, Object> record = records.get(i);
			for (Aggregator aggregator : aggregators) {
				seriesDataList.add(new PieData(String.valueOf(record.get(groups.get(0).getName())), record.get(aggregator.getName())));
			}

		}
		resultMap.put("legend", legend);
		resultMap.put("seriesDataList", seriesDataList);
		return resultMap;
	}

	private Map<String, Object> getMapsData(String id, ViewExecuteParam executeParam) {
		Page<Map<String, Object>> resultPage = getData(id, executeParam);
		Map<String, Object> resultMap = Maps.newHashMap();
		List<Map<String, Object>> dataList = resultPage.getRecords();
		List<Map<String, Object>> resultList = Lists.newArrayList();
		List<Aggregator> aggregatorList = executeParam.getAggregators();
		double min = 0;
		double max = 100;
		List<Group> groups = executeParam.getGroups();
		for (int i = 0; i < dataList.size(); i++) {
			Map<String, Object> record = dataList.get(i);
			Map<String, Object> m = Maps.newHashMap();
			if (groups.size() > 0) {
				m.put("name", record.get(groups.get(0).getName()));
			}
			if (aggregatorList.size() > 0) {
				Aggregator aggregator = aggregatorList.get(0);
				double value = Double.parseDouble(String.valueOf(record.get(aggregator.getName())));
				m.put("value", value);
				if (min > value) {
					min = value;
				}
				if (value > max) {
					max = value;
				}
			}
			resultList.add(m);

		}
		if (max > 100) {
			max = ((int) ((max + 100) / 100)) * 100;
		}
		resultMap.put("min", min);
		resultMap.put("max", max);
		resultMap.put("dataList", dataList);
		return resultMap;
	}

	/**
	 * 这里希望聚合函数不要影响到返回字段便于使用
	 *
	 * @param id
	 * @param executeParam
	 * @return
	 */
	private Map<String, Object> getTextData(String id, ViewExecuteParam executeParam) {
		Page<Map<String, Object>> resultPage = getData(id, executeParam);
		Map<String, Object> resultMap = Maps.newHashMap();
		List<Map<String, Object>> resultList = Lists.newArrayList();
		List<Map<String, Object>> dataList = resultPage.getRecords();
		dataList.forEach(data -> {
			Map<String, Object> m = Maps.newHashMap();
			List<Aggregator> aggregatorList = executeParam.getAggregators();
			aggregatorList.forEach(aggregator -> {
				m.put(aggregator.getColumn(), data.get(aggregator.getName()));
			});
			resultList.add(m);
		});
		resultMap.put("dataList", resultList);
		return resultMap;
	}

	private Map<String, Object> getGaugeData(String id, ViewExecuteParam executeParam) {
		Page<Map<String, Object>> resultPage = getData(id, executeParam);
		List<Map<String, Object>> records = resultPage.getRecords();
		Map<String, Object> resultMap = Maps.newHashMap();
		List<Group> groups = executeParam.getGroups();
		List<Aggregator> aggregators = executeParam.getAggregators();
		List<PieData> dataList = Lists.newArrayList();
		double min = 0;
		double max = 100;
		for (int i = 0; i < records.size(); i++) {
			Map<String, Object> record = records.get(i);
			for (Aggregator aggregator : aggregators) {
				double value = Double.parseDouble(String.valueOf(record.get(aggregator.getName())));
				dataList.add(new PieData(String.valueOf(record.get(groups.get(0).getName())), record.get(aggregator.getName())));
				if (min > value) {
					min = value;
				}
				if (value > max) {
					max = value;
				}
			}

		}
		if (max > 100) {
			max = ((int) ((max + 100) / 100)) * 100;
		}
		resultMap.put("min", min);
		resultMap.put("max", max);
		resultMap.put("dataList", dataList);
		return resultMap;
	}

	private Map<String, Object> getWordCloudData(String id, ViewExecuteParam executeParam) {
		Page<Map<String, Object>> resultPage = getData(id, executeParam);
		List<Map<String, Object>> records = resultPage.getRecords();
		Map<String, Object> resultMap = Maps.newHashMap();
		List<Group> groups = executeParam.getGroups();
		List<Aggregator> aggregators = executeParam.getAggregators();
		List<PieData> dataList = Lists.newArrayList();
		for (int i = 0; i < records.size(); i++) {
			Map<String, Object> record = records.get(i);
			for (Aggregator aggregator : aggregators) {
				dataList.add(new PieData(String.valueOf(record.get(groups.get(0).getName())), record.get(aggregator.getName())));
			}

		}
		resultMap.put("dataList", dataList);
		return resultMap;
	}

	private Map<String, Object> getFunnelData(String id, ViewExecuteParam executeParam) {
		Page<Map<String, Object>> resultPage = getData(id, executeParam);
		List<Map<String, Object>> records = resultPage.getRecords();
		Map<String, Object> resultMap = Maps.newHashMap();
		List<String> legend = Lists.newArrayList();
		List<String> xAxisData = Lists.newArrayList();
		List<String> yAxisData = Lists.newArrayList();
		List<Group> groups = executeParam.getGroups();
		List<Aggregator> aggregators = executeParam.getAggregators();
		//一个指标对应一个Data
		Map<String, SeriesData> seriesDataMap = Maps.newHashMap();
		List<SeriesData> seriesDataList = Lists.newArrayList();
		for (Aggregator aggregator : aggregators) {
			SeriesData seriesData = new SeriesData();
			seriesData.setName(aggregator.getDisplayName());
			seriesData.setDataList(Lists.newArrayList());
			seriesDataMap.put(aggregator.getName(), seriesData);
			seriesDataList.add(seriesData);
			legend.add(aggregator.getDisplayName());
		}
		for (int i = 0; i < records.size(); i++) {
			Map<String, Object> record = records.get(i);
			for (Aggregator aggregator : aggregators) {
				seriesDataMap.get(aggregator.getName()).getDataList().add(new PieData(String.valueOf(record.get(groups.get(0).getName())), record.get(aggregator.getName())));
			}
			legend.add(String.valueOf(record.get(groups.get(0).getName())));

		}
		resultMap.put("legend", legend);
		resultMap.put("seriesDataList", seriesDataList);
		return resultMap;
	}

	private Map<String, Object> getRadarData(String id, ViewExecuteParam executeParam) {
		Page<Map<String, Object>> resultPage = getData(id, executeParam);
		List<Map<String, Object>> records = resultPage.getRecords();
		Map<String, Object> resultMap = Maps.newHashMap();
		List<String> legend = Lists.newArrayList();
		List<Group> groups = executeParam.getGroups();
		List<Aggregator> aggregators = executeParam.getAggregators();
		//一个指标对应一个Data
		Map<String, SeriesData> seriesDataMap = Maps.newHashMap();
		List<SeriesData> seriesDataList = Lists.newArrayList();
		List<Map<String, Object>> indicator = Lists.newArrayList();
		for (Aggregator aggregator : aggregators) {
			SeriesData seriesData = new SeriesData();
			seriesData.setName(aggregator.getDisplayName());
			seriesData.setDataList(Lists.newArrayList());
			seriesDataMap.put(aggregator.getName(), seriesData);
			seriesDataList.add(seriesData);
			legend.add(aggregator.getDisplayName());
		}
		for (int i = 0; i < records.size(); i++) {
			Map<String, Object> record = records.get(i);
			for (Aggregator aggregator : aggregators) {
				seriesDataMap.get(aggregator.getName()).getDataList().add(record.get(aggregator.getName()));
			}
			indicator.add(ImmutableMap.of("name", record.get(groups.get(0).getName())));

		}
		resultMap.put("legend", legend);
		resultMap.put("indicator", indicator);
		resultMap.put("seriesDataList", seriesDataList);
		return resultMap;
	}

	private Map<String, Object> getPieData(String id, ViewExecuteParam executeParam) {
		Page<Map<String, Object>> resultPage = getData(id, executeParam);
		List<Map<String, Object>> records = resultPage.getRecords();
		Map<String, Object> resultMap = Maps.newHashMap();
		List<String> legend = Lists.newArrayList();
		List<Group> groups = executeParam.getGroups();
		List<Aggregator> aggregators = executeParam.getAggregators();
		//一个指标对应一个Data
		Map<String, SeriesData> seriesDataMap = Maps.newHashMap();
		List<SeriesData> seriesDataList = Lists.newArrayList();
		for (Aggregator aggregator : aggregators) {
			SeriesData seriesData = new SeriesData();
			seriesData.setName(aggregator.getDisplayName());
			seriesData.setDataList(Lists.newArrayList());
			seriesDataMap.put(aggregator.getName(), seriesData);
			seriesDataList.add(seriesData);
			legend.add(aggregator.getDisplayName());
		}
		for (int i = 0; i < records.size(); i++) {
			Map<String, Object> record = records.get(i);
			for (Aggregator aggregator : aggregators) {
				seriesDataMap.get(aggregator.getName()).getDataList().add(new PieData(String.valueOf(record.get(groups.get(0).getName())), record.get(aggregator.getName())));
			}

		}
		resultMap.put("legend", legend);
		resultMap.put("seriesDataList", seriesDataList);
		return resultMap;
	}

	private Map<String, Object> getLineData(String id, ViewExecuteParam executeParam) {
		Page<Map<String, Object>> resultPage = getData(id, executeParam);
		List<Map<String, Object>> records = resultPage.getRecords();
		Map<String, Object> resultMap = Maps.newHashMap();
		List<String> legend = Lists.newArrayList();
		List<String> xAxisData = Lists.newArrayList();
		List<String> yAxisData = Lists.newArrayList();
		List<Group> groups = executeParam.getGroups();
		List<Aggregator> aggregators = executeParam.getAggregators();
		//一个指标对应一个Data
		Map<String, SeriesData> seriesDataMap = Maps.newHashMap();
		List<SeriesData> seriesDataList = Lists.newArrayList();
		for (Aggregator aggregator : aggregators) {
			SeriesData seriesData = new SeriesData();
			seriesData.setName(aggregator.getDisplayName());
			seriesData.setAxisIndex(aggregator.getAxisIndex());
			seriesData.setDataList(Lists.newArrayList());
			seriesDataMap.put(aggregator.getName(), seriesData);
			seriesDataList.add(seriesData);
			legend.add(aggregator.getDisplayName());
		}
		for (int i = 0; i < records.size(); i++) {
			Map<String, Object> record = records.get(i);
			for (Aggregator aggregator : aggregators) {
				seriesDataMap.get(aggregator.getName()).getDataList().add(record.get(aggregator.getName()));
			}
			xAxisData.add(String.valueOf(record.get(groups.get(0).getName())));

		}
		resultMap.put("legend", legend);
		resultMap.put("xAxisData", xAxisData);
		resultMap.put("seriesDataList", seriesDataList);
		return resultMap;
	}
}
